<?
/*-- Project Introduce --*/



class JCAT_FSPackageFile extends JCAT_FSPackageElement
{
	const XMLNODENAME = 'f' ;
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	void
	 */
	public function Pack($nPos,$hPackage,$sTempFolder)
	{
		$sFileSource = $this->GetPath() ;
		if(!$sFileSource)
		{
			return ;
		}

		$nPackLength = 0 ;
		if( $nCompressLevel = $this->GetCompressLevel() )
		{
			// 检查 zlib
			JCAT_FSPackageOperatorBase::CheckZLib() ;
			
			// 创建一个临时文件，并将源文件压缩至临时文件
			$sTempComressedFile = $sTempFolder.'/'.microtime(true).rand(0,999) ;
			
			$aZip = new ZipArchive;
			$hZip = $aZip->open($sTempComressedFile, ZipArchive::CREATE);
			$hZip->addFile($sFileSource,'thefile') ;
			$hZip->close() ;
			
			// 合并压缩文件
			if( !$hTemp=fopen($sTempComressedFile,'rb') )
			{
				throw new JCAT_Exception("无法读取文件：".$sTempComressedFile) ;
			}
		
			while( $sData=fread($hTemp,10240) )
			{
				$nPackLength+= strlen($sData) ;
				fwrite($hPackage,$sData) ;
			}
			
			fclose($hTemp) ;
			unlink($sTempComressedFile) ;
		}
		
		else 
		{
		
			$hFile = fopen($sFileSource,'rb') ;
			if(!$hFile)
			{
				throw new JCAT_Exception("无法读取文件：".$sFileSource) ;
			}
		
			while( $sData=fread($hFile,10240) )
			{
				$nPackLength+= strlen($sData) ;
				fwrite($hPackage,$sData) ;
			}
			
			fclose($hFile) ;
		}
		
		// 记录长度
		$this->nLength = $nPackLength ;
		$this->nPos = $nPos ;
	}
	

	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sToFolder
	 * @param	$hPackage	handle
	 * @return	void
	 */
	public function Release($sToFolder,$hPackage,$nOffset,$nReleaseFlg=JCAT_FSPackage::RELEASE_DEFAULT) 
	{
		$sPath = $sToFolder.'/'.$this->GetName() ;
		if( file_exists($sPath) )
		{
			if(!($nReleaseFlg&JCAT_FSPackage::RELEASE_OVERWRITE))
			{
				throw new JCAT_Exception("路径已经存在：".$sPath) ;
			} 

			if( is_dir($sPath) )
			{
				throw new JCAT_Exception("路径是一个已经存在的目录，无法将文件覆盖释放到路径上：".$sPath) ;
			}
			
			if( !is_writable($sPath) )
			{
				throw new JCAT_Exception("文件已经存在，缺少权限对其进行覆盖：".$sPath) ;
			}
		}
		
		else 
		{
			if( !is_writable($sToFolder) )
			{
				throw new JCAT_Exception("无法将文件释放到目录中：".$sToFolder) ;
			}
		}
		
		// 包内定位
		fseek($hPackage,$this->nPos+$nOffset) ;
		
		// 需要解压缩
		if( $this->nCompressLevel )
		{
			// 检查 zlib
			JCAT_FSPackageOperatorBase::CheckZLib() ;
			
			// 释放到临时文集
			$sTempFile = $sToFolder.'/'.md5(microtime()).rand(0,999);
			$this->ReleaseToFile($hPackage,$sTempFile);
			
			$aZip = new ZipArchive() ;
			
			// 解压缩
			$hTempFile = gzopen($sTempFile,'r'.$this->nCompressLevel) ;
			$hFile = fopen($sPath,'wb') ;
			
			while($sData=gzread($hTempFile,10240))
			{
				fwrite($hFile,$sData) ;
			}
			
			fclose($hFile) ;
			fclose($hTempFile) ;
			unlink($sTempFile) ;
		}
		
		// 直接释放
		else
		{
			$this->ReleaseToFile($hPackage,$sPath);
		}
	}
	
	/**
	 * Description
	 *
	 * @access	private
	 * @return	void
	 */
	private function ReleaseToFile($hPackage,$sToPath)
	{
		$hFile = fopen($sToPath,'wb') ;
		if(!$hFile)
		{
			throw new JCAT_Exception("无法打开文件：".$sToPath) ;
		}
		
		$nLen = $this->GetLength() ;
		while( $nLen>0 )
		{
			$sData = fread($hPackage,10240) ;
			$nLen-= 10240 ;
			
			if(!$sData)
			{
				throw new JCAT_Exception("从文件包中读取文件出错。") ;
			}
			
			fwrite($hFile,$sData) ;
		}
		
		fclose($hFile) ;
	}
	
	/**
	 * 设置对象属性 self::$nPackFlag
	 *
	 * @access	public
	 * @param	$nPackFlag		int	打包方式
	 * @return	int
	 */
	public function SetPackFlag($nPackFlag)
	{
		$this->nPackFlag = intval($nPackFlag) ;
	}
	
	/**
	 * 取得对象属性 self::$nPackFlag
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetPackFlag()
	{
		return $this->nPackFlag ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetLength()
	{
		return $this->nLength ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$aXMLDoc		SimpleXMLElement
	 * @return	void
	 */
	public function LoadFromXMLDoc(SimpleXMLElement $aXMLDoc)
	{
		$this->SetName(strval($aXMLDoc['name'])) ;
		$this->nLength = intval($aXMLDoc['len']) ;
		$this->nPos = intval($aXMLDoc['pos']) ;
	
		$this->sMD5 = isset($aXMLDoc['md5'])?
					strval($aXMLDoc['md5']): null ;
		
		$this->nCompressLevel = isset($aXMLDoc['compress'])?
					intval($aXMLDoc['compress']): 0 ;
	}
	
	/**
	 * 是否校验文件完整性
	 *
	 * @access	public
	 * @return	bool
	 */
	public function GetVerify()
	{
		return ($this->GetPackFlag()&JCAT_FilePacker::PACK_VERIFY)? true: false ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetCompressLevel()
	{
		return ($this->GetPackFlag()&JCAT_FilePacker::PACK_COMPRESSED) ;
	}

	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetPieceLength()
	{
		return pow(4,($this->GetPackFlag()>>5))*1024 ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	string
	 */
	public function MakeXML()
	{
		$sNodeName = self::XMLNODENAME ;
		$sFilename = $this->GetName() ;
		
		$sXML = "<{$sNodeName} name=\"{$sFilename}\" len=\"{$this->nLength}\" pos=\"{$this->nPos}\"" ;
		
		// 校验文件
		if($this->GetVerify())
		{
			$sPath = $this->GetPath() ;
			$this->sMD5 = md5_file($sPath) ;
			if(!$this->sMD5)
			{
				throw new JCAT_Exception('文件路径不存在：'.$sPath) ;
			}
			$sXML.= " md5=\"{$this->sMD5}\"" ;
		}
		
		// 压缩
		$this->nCompressLevel = $this->GetCompressLevel() ;
		if( $this->nCompressLevel )
		{
			$sXML.= " compress=\"{$this->nCompressLevel}\"" ;
		}
		
		$sXML.= " />" ;
		
		return $sXML ;
	}
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nLength = 0 ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nPos = 0 ;
	
	
	/**
	 * 对象属性 打包方式
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nPackFlag = JCAT_FilePacker::PACK_DEFAULT ;
	
	
	private $nCompressLevel = 0 ;
	private $sMD5 = 0 ;
}


?>